package com.alibaba.cloud.ddd.infrastructure.messaging.jms;

import java.util.List;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsOperations;
import org.springframework.jms.core.JmsTemplate;

import com.alibaba.cloud.ddd.application.ApplicationEvents;
import com.alibaba.cloud.ddd.application.CargoInspectionService;
import com.alibaba.cloud.ddd.application.HandlingEventService;
import com.rabbitmq.jms.admin.RMQConnectionFactory;
import com.rabbitmq.jms.admin.RMQDestination;
import com.rabbitmq.jms.client.RMQConnection;

import jakarta.jms.Connection;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.Destination;
import jakarta.jms.JMSException;
import jakarta.jms.MessageConsumer;
import jakarta.jms.QueueConnection;
import jakarta.jms.Session;

@EnableJms
@Configuration
public class InfrastructureMessagingJmsConfig {

	@Value("${brokerUrl}")
	private String brokerUrl;

	@Bean(value = "cargoHandledConsumer", destroyMethod = "close")
	public MessageConsumer cargoHandledConsumer(Session session,
			@Qualifier("cargoHandledQueue") Destination destination, CargoInspectionService cargoInspectionService)
			throws JMSException {
		MessageConsumer consumer = session.createConsumer(destination);
		consumer.setMessageListener(new CargoHandledConsumer(cargoInspectionService));
		return consumer;
	}

	@Bean(value = "handlingEventRegistrationAttemptConsumer", destroyMethod = "close")
	public MessageConsumer handlingEventRegistrationAttemptConsumer(Session session,
			@Qualifier("handlingEventRegistrationAttemptQueue") Destination destination,
			HandlingEventService handlingEventService) throws JMSException {
		MessageConsumer consumer = session.createConsumer(destination);
		consumer.setMessageListener(new HandlingEventRegistrationAttemptConsumer(handlingEventService));
		return consumer;
	}

	@Bean(value = "misdirectedCargoConsumer", destroyMethod = "close")
	public MessageConsumer misdirectedCargoConsumer(Session session,
			@Qualifier("misdirectedCargoQueue") Destination destination) throws JMSException {
		MessageConsumer consumer = session.createConsumer(destination);
		consumer.setMessageListener(new SimpleLoggingConsumer());
		return consumer;
	}

	@Bean(value = "deliveredCargoConsumer", destroyMethod = "close")
	public MessageConsumer deliveredCargoConsumer(Session session,
			@Qualifier("deliveredCargoQueue") Destination destination) throws JMSException {
		MessageConsumer consumer = session.createConsumer(destination);
		consumer.setMessageListener(new SimpleLoggingConsumer());
		return consumer;
	}

	@Bean(value = "rejectedRegistrationAttemptsConsumer", destroyMethod = "close")
	public MessageConsumer rejectedRegistrationAttemptsConsumer(Session session,
			@Qualifier("rejectedRegistrationAttemptsQueue") Destination destination) throws JMSException {
		MessageConsumer consumer = session.createConsumer(destination);
		consumer.setMessageListener(new SimpleLoggingConsumer());
		return consumer;
	}

	@Bean
	public HandlingEventRegistrationAttemptConsumer handlingEventRegistrationAttemptConsumer(
			HandlingEventService handlingEventService) {
		return new HandlingEventRegistrationAttemptConsumer(handlingEventService);
	}

	@Bean("cargoHandledQueue")
	public Destination cargoHandledQueue() throws Exception {
		return createQueue("CargoHandledQueue");
	}

	@Bean("misdirectedCargoQueue")
	public Destination misdirectedCargoQueue() throws Exception {
		return createQueue("MisdirectedCargoQueue");
	}

	@Bean("deliveredCargoQueue")
	public Destination deliveredCargoQueue() throws Exception {
		return createQueue("DeliveredCargoQueue");
	}

	@Bean("handlingEventRegistrationAttemptQueue")
	public Destination handlingEventRegistrationAttemptQueue() throws Exception {
		return createQueue("HandlingEventRegistrationAttemptQueue");
	}

	@Bean("rejectedRegistrationAttemptsQueue")
	public Destination rejectedRegistrationAttemptsQueue() throws Exception {
		return createQueue("RejectedRegistrationAttemptsQueue");
	}

	@Bean
	public DefaultJmsListenerContainerFactory listenerContainerFactory(ConnectionFactory jmsConnectionFactory) {
		DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
		factory.setConnectionFactory(jmsConnectionFactory);
		factory.setConcurrency("1-1");
		return factory;
	}

	@Bean
	public ConnectionFactory jmsConnectionFactory() {
		RMQConnectionFactory factory = new RMQConnectionFactory();
		factory.setTrustedPackages(
				List.of("se.citerus.dddsample.interfaces.handling", "se.citerus.dddsample.domain", "java.util"));
		return factory;
	}

	@Bean
	public JmsOperations jmsOperations(ConnectionFactory jmsConnectionFactory) {
		return new JmsTemplate(jmsConnectionFactory);
	}

	@Bean(destroyMethod = "close")
	public Connection connection(ConnectionFactory connectionFactory) throws JMSException {
		QueueConnection queueConnection = ((RMQConnectionFactory) connectionFactory).createQueueConnection();
		queueConnection.start();
		return queueConnection;
	}

	@Bean
	public Session session(Connection connection) throws JMSException {
		return ((RMQConnection) connection).createSession(false, Session.AUTO_ACKNOWLEDGE);
	}

	@Bean
	public ApplicationEvents applicationEvents(JmsOperations jmsOperations,
			@Qualifier("cargoHandledQueue") Destination cargoHandledQueue,
			@Qualifier("misdirectedCargoQueue") Destination misdirectedCargoQueue,
			@Qualifier("deliveredCargoQueue") Destination deliveredCargoQueue,
			@Qualifier("rejectedRegistrationAttemptsQueue") Destination rejectedRegistrationAttemptsQueue,
			@Qualifier("handlingEventRegistrationAttemptQueue") Destination handlingEventRegistrationAttemptQueue) {
		return new JmsApplicationEventsImpl(jmsOperations, cargoHandledQueue, misdirectedCargoQueue,
				deliveredCargoQueue, rejectedRegistrationAttemptsQueue, handlingEventRegistrationAttemptQueue);
	}

	private Destination createQueue(String queueName) {
		return new RMQDestination();
	}
}
